package 函数式编程;

//Lambda 表达式，也可称为闭包，它是推动 Java 8 发布的最重要新特性。
//Lambda 允许把函数作为一个方法的参数（函数作为参数传递进方法中）。
//使用 Lambda 表达式可以使代码变的更加简洁紧凑。
//
//一、lambda 表达式的语法格式如下：
//标准格式：（形式参数）->{代码块}
//Lambda表达式组成三要素：括号，箭头，代码块
//
//Lambda表达式的代码分析
//●  (): 里面没有内容，可以看成是方法形式参数为空
//●  ->:用箭头指向后面要做的事情
//●  {}: 包含一段代码，我们称之为代码块，可以看成是方法体中的内容
//
//Lambda表达式的核心就是实现的这个接口中的抽象方法中的 “形参列表 -> 抽象方法的处理”。
//
//实例如下，用匿名内部类和Lambda表达式分别启动一个线程，相对于匿名内部类，我们会发现 Lambda 表达式更简洁
//
//public class RunableDemo01 {
//    public static void main(String[] args) {
//        //匿名内部类的方式启动一个线程
//        new Thread(new Runnable() {
//            @Override
//            public void run() {
//                System.out.println(Thread.currentThread().getName()+"线程启动啦");
//            }
//        }).start();
//        
//			Lambda表达式的方式启动一个线程
//       new Thread(()-> System.out.println(Thread.currentThread().getName()+"线程启动啦")).start();
//    }
//}

//二、Lambda表达式具体实例：
//Lambda表达式使用前提：
//  ●有一个接口
//  ●接口中有且仅有一个抽象方法
//实例一：
//Lambda表达式是: 方法 在调用接口中抽象方法时 直接实现接口中的抽象方法（如下）  实现的这个接口中的抽象方法中的 “形参列表 -> 抽象方法的处理”
/*
public class 使用Lambda {
	private static void useLamDemo(LamDemoImpl la) {
		la.fun();
	}
	public static void main(String[] args) {
				
		LamDemoImpl lamDemoImpl=new LamDemoImpl() {		
			@Override
			public void fun() {
				System.out.println("Lambda表达式！");				
			}
		};
		lamDemoImpl.fun();
		
		LamDemoImpl lamDemoImpl1= ()->System.out.println("Lambda表达式！");//实现的这个接口中的抽象方法中的 “形参列表 -> 抽象方法的处理”
		lamDemoImpl1.fun();
		
		//在方法调用时实现方法参数所代表的接口方法
		// useLamDemo(()->{
		// System.out.println("Lambda表达式！");
		// });
		useLamDemo(() -> System.out.println("Lambda表达式！"));

	}
}

interface LamDemoImpl {
	void fun();
}
*/
//实例二
//这个是抽象方法带参时Lambda表达式如何传参（如下）
/*
public class 使用Lambda {
	public static void main(String[] args) {
		Addimpl addimpl = new Addimpl() {			
			@Override
			public int add(int x,int y,int z) {
				int xyz=x+y+z;
				System.out.println("---计算结果："+xyz);
				return xyz;
			}
		};
		int num = addimpl.add(2, 5, 3);

		Addimpl addimpl1=(x,y,z) -> {int xyz=x+y+z;System.out.println("===计算结果:"+xyz);return xyz;};
		int num1=addimpl1.add(2, 5, 3);
	
		add2((x, y, z) -> {//不需要声明参数类型，编译器可以统一识别参数值
			return x + y + z;
		});
		
		
		add2((int x, int y, int z) -> {
			return x + y + z;
		});
		
		add2((int x,int y,int z)-> x+y+z);//亦可   实现的这个接口中的抽象方法中的 “形参列表 -> 抽象方法的处理”
	}

	private static void add2(Addimpl n) {
		System.out.println("nnnnnnnn");
		int num = n.add(2, 5, 3);
		System.out.println("结果是：" + num);
	}
}

interface Addimpl {
	int add(int a, int b, int c);//因为接口定义的所有方法默认都是public abstract的，所以这两个修饰符不需要写出来（写不写效果都一样）。
}
*/
//实例三：
//Lambda表达式在一些情况下是可以省略简写的（如下）
//接口：
/*interface LamDemoImpl {
    void fun(String s);
}
//测试类：
public class 使用Lambda {
    public static void main(String[] args) {
        useFun((String s)->{
            System.out.println("1: "+s);
        });
        //参数的类型可以省略
        useFun((s)->{ System.out.println("2: "+s);});
        //单个参数可以省略括号
        useFun(s-> {System.out.println("3: "+s);});
        //代码块中单个语句可以省略大括号
        useFun(s-> System.out.println("4: "+s));
    }

    private static void useFun(LamDemoImpl la) {
        la.fun("如何省略呢");
    }
}*/
//
//省略的几种情况：
//
//可选类型声明：不需要声明参数类型，编译器可以统一识别参数值。
//可选的参数圆括号：一个参数无需定义圆括号，但多个参数需要定义圆括号。
//可选的大括号：如果主体包含了一个语句，就不需要使用大括号。
//可选的返回关键字：如果主体只有一个表达式返回值则编译器会自动返回值，大括号需要指定明表达式返回了一个数值。
//Lambda 表达式使用注意事项：
//
//使用Lambda表达式必须要有接口，并且接口中有且只有一个抽象方法
//必须有上下文环境，才能推导出Lambda表达式对应的接口
//根据局部变量的赋值知Lambda对应的接口: Runnabler=() -> System.out.println("Lambda表达式");
//根据调用方法的参数得知Lambda对应的接口: new Thread() -> System.out.printIn("Lambda表达式).start();


//Java Lambda 表达式语法
//
//lambda 表达式的语法格式如下：
//
//   (parameters) -> expression  或  (parameters) ->{ statements; }
//   (   参数    ) -> 表达式       或  (    参数   ) -> {    陈述      }
//   以下是lambda表达式的重要特征:
//
//  ●可选类型声明：不需要声明参数类型，编译器可以统一识别参数值。
//  ●可选的参数圆括号：()一个参数无需定义圆括号，但多个参数需要定义圆括号。
//  ●可选的大括号：如果主体包含了一个语句，就不需要使用大括号。
//  ●可选的返回关键字：如果主体只有一个表达式返回值则编译器会自动返回值，大括号需要指定表达式返回了一个数值。
//
//  Lambda 表达式实例
//
//  Lambda 表达式的简单例子:
//
//1. 不需要参数,返回值为 5  
//() -> 5  
//  
//2. 接收一个参数(数字类型),返回其2倍的值  一个参数无需定义圆括号
//x -> 2 * x  
//  
//3. 接受2个参数(数字),并返回他们的差值  
//(x, y) -> x – y  
//  
//4. 接收2个int型整数,返回他们的和  
//(int x, int y) -> x + y  
//  
//// 5. 接受一个 string 对象,并在控制台打印,不返回任何值(看起来像是返回void)  
//(String s) -> System.out.print(s)

public class 使用Lambda {
   public static void main(String args[]){
	   使用Lambda tester = new 使用Lambda();//可设置静态方法，则不需要实例化成tester，直接使用静态方法
      // 类型声明

	   //MathOperation addition = (int a, int b) -> a + b;//与下面语句等价：
	   MathOperation addition=new MathOperation(){//加法运算操作接口实现
		   public int operation(int a,int b) {
            return a+b;
		   }
      };

      // 不用类型声明
      MathOperation subtraction = (a, b) -> a - b;//减法运算操作接口实现
      // 大括号中的返回语句
      MathOperation multiplication = (int a, int b) -> { return a * b; };//乘法运算操作接口实现
      // 没有大括号及返回语句
      MathOperation division = (int a, int b) -> a / b;//除法运算操作接口实现
      System.out.println("10 + 5 = " + tester.operate(10, 5, addition));
//      System.out.println("10 + 5 = " + tester.operate(10, 5, (int a, int b) -> a + b));
//      System.out.println("10 + 5 = " + operate(10, 5, addition));//如设成静态方法，则不需要实例化成tester
      System.out.println("10 - 5 = " + tester.operate(10, 5, subtraction));
      System.out.println("10 x 5 = " + tester.operate(10, 5, multiplication));
      System.out.println("10 / 5 = " + tester.operate(10, 5, division));
      // 不用括号
      GreetingService greetService1 = message ->System.out.println("Hello " + message);//显示操作接口实现
      // 用括号
      GreetingService greetService2 = (message) -> System.out.println("Hello " + message);
      greetService1.sayMessage("Runoob");
      greetService2.sayMessage("Google");
   }
   interface MathOperation {//可供两个操作数之间加、减、乘、除运算的接口实现
      int operation(int a, int b);
   }
   interface GreetingService {
      void sayMessage(String message);
   }
   private int operate(int a, int b, MathOperation mathOperation){
//   private static int operate(int a, int b, MathOperation mathOperation){//如设成静态方法，则不需要实例化成tester
      return mathOperation.operation(a, b);
   }
}

//使用 Lambda 表达式需要注意以下两点：
//
//  ●Lambda 表达式主要用来定义行内执行的方法类型接口(FunctionalInterface:单方法的接口)，例如，一个简单方法接口。在上面例子中，我们
//				使用各种类型的Lambda表达式来定义MathOperation接口的方法。然后我们定义了sayMessage的执行。
//  ●Lambda 表达式免去了使用匿名方法的麻烦，并且给予Java简单但是强大的函数化的编程能力。

//变量作用域
//lambda 表达式只能引用标记了 final 的外层局部变量，这就是说不能在 lambda 内部修改定义在域外的局部变量，否则会编译错误。
//
//在 Java8Tester.java 文件输入以下代码：
//
//Java8Tester.java 文件
//public class Java8Tester {
// 
//   final static String salutation = "Hello! ";
//   
//   public static void main(String args[]){
//      GreetingService greetService1 = message ->System.out.println(salutation + message);
//      greetService1.sayMessage("Runoob");
//   }
//    
//   interface GreetingService {
//      void sayMessage(String message);
//   }
//}
//执行以上脚本，输出结果为：
//
//$ javac Java8Tester.java 
//$ java Java8Tester
//Hello! Runoob
//我们也可以直接在 lambda 表达式中访问外层的局部变量：
//
//Java8Tester.java 文件
//public class Java8Tester {
//    public static void main(String args[]) {
//        final int num = 1;
//        Converter<Integer, String> s = (param) -> System.out.println(String.valueOf(param + num));
//        s.convert(2);  // 输出结果为 3
//    }
// 
//    public interface Converter<T1, T2> {
//        void convert(int i);
//    }
//}
//lambda 表达式的局部变量可以不用声明为 final，但是必须不可被后面的代码修改（即隐性的具有 final 的语义）
//
//int num = 1;  
//Converter<Integer, String> s = (param) -> System.out.println(String.valueOf(param + num));
//s.convert(2);
//num = 5;  
////报错信息：Local variable num defined in an enclosing scope must be final or effectively 
// final
//在 Lambda 表达式当中不允许声明一个与局部变量同名的参数或者局部变量。
//
//String first = "";  
//Comparator<String> comparator = (first, second) -> Integer.compare(first.length(), second.length());  //编译会出错 

